package cn.wizzer.app.crown.commons.websocket.main.base;

import cn.wizzer.app.crown.modules.models.constant.Fields;
import cn.wizzer.app.crown.commons.websocket.main.action.BaseNettyAction;
import cn.wizzer.app.crown.commons.websocket.main.action.NettyAction;
import cn.wizzer.app.crown.commons.websocket.main.exception.NettyException;
import cn.wizzer.app.crown.commons.websocket.main.filter.BaseNettyFilter;
import cn.wizzer.app.netty.modules.models.NettyConstant;
import cn.wizzer.app.user.modules.models.constant.ChatConstant;
import cn.wizzer.framework.util.Result;
import cn.wizzer.framework.base.exceptions.BizException;
import cn.wizzer.framework.base.model.enums.BizExceptionEnum;
import cn.wizzer.framework.util.JSONUtil;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.util.concurrent.GlobalEventExecutor;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.Strings;
import org.nutz.log.Log;
import org.nutz.log.Logs;

import java.util.HashMap;
import java.util.Map;

@IocBean(name = "textWebSocketFrameHandler")
@ChannelHandler.Sharable
public class TextWebSocketFrameHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {
    private static final Log log = Logs.get();

    private static final Map<String, ChannelGroup> CHANNEL_GROUP_MAP = new HashMap<>();

    static {
        //启动时创建默通道组
        CHANNEL_GROUP_MAP.put(ChatConstant.DEFAULT, new DefaultChannelGroup(GlobalEventExecutor.INSTANCE));
    }

    public static int countChannel(String key) {
        ChannelGroup channels = CHANNEL_GROUP_MAP.get(key);
        return channels != null ? channels.size() : 0;
    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx,
                                TextWebSocketFrame msg) throws Exception {
        Channel income = ctx.channel();
        try {
            String uri = msg.text();
            NettyRouter router = new NettyRouter(uri);
            router.setIncome(income);
            //业务逻辑实现类
            BaseNettyAction action = BaseNettyAction.instance(router.getUri());
            //接口注解
            NettyAction nettyAction = action.getClass().getAnnotation(NettyAction.class);
            //拦截器-接口指定
            Class[] fClasses = nettyAction.filters();
            //拦截器校验
            if (fClasses.length > 0) {
                for (Class filterClass : fClasses) {
                    Object result = BaseNettyFilter.instance(filterClass).doFilter(router, action);
                    if (result != null) {
                        send(income, result);
                        return;
                    }
                }
            }

            //region Subscribe订阅
            String subscribeParam = router.getParam(NettyConstant.SUBSCRIBE);
            if (Strings.isNotBlank(subscribeParam)) {
                //取消订阅
                if (Fields.TRUE_STR.equals(router.getParam(NettyConstant.CANCEL_SUBSCRIBE))) {
                    send(income, removeSubscribe(income, router, subscribeParam));
                } else {
                    Result result = action.subscribe(router);
                    //添加订阅
                    if (result.getCode() == 0) {
                        addSubscribe(income, router, subscribeParam);
                    }
                    send(income, result);
                }
            }
            //endregion
            //region Real Time实时
            else {
                Object result = action.action(router.getParams());
                send(income, result);
            }
            //endregion
        } catch (BizException be) {
            send(income, Result.error().setMsg(be.getRemand()));
        } catch (RuntimeException re) {
            //异常处理
            send(income, NettyException.doException(re));
            re.printStackTrace();
        } catch (Exception e) {
            log.error(e);
        }
    }

    /**
     * 添加订阅
     *
     * @param income
     * @param router
     * @param subscribeParam
     */
    private void addSubscribe(Channel income, NettyRouter router, String subscribeParam) {
        String key = router.getUri() + ":" + subscribeParam;
        ChannelGroup channels = CHANNEL_GROUP_MAP.get(key);
        if (channels == null) {
            channels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
        }
        channels.add(income);
        CHANNEL_GROUP_MAP.put(key, channels);
    }

    /**
     * 取消订阅
     *
     * @param income
     * @param router
     * @param subscribeParam
     */
    private Result removeSubscribe(Channel income, NettyRouter router, String subscribeParam) {
        ChannelGroup channels = CHANNEL_GROUP_MAP.get(router.getUri() + ":" + subscribeParam);
        if (channels != null && channels.remove(income)) {
            return Result.success();
        }
        return Result.error(BizExceptionEnum.NOT_SUBSCRIBED);
    }

    /**
     * 发送消息
     *
     * @param channel
     * @param data
     */
    private static void send(Channel channel, Object data) {
        if (data instanceof String) {
            channel.writeAndFlush(new TextWebSocketFrame(data.toString()));
        } else {
            channel.writeAndFlush(new TextWebSocketFrame(JSONUtil.toJSONString(data)));
        }
    }

    /**
     * 订阅通知
     *
     * @param groupKey
     * @param data
     */
    public static void send(String groupKey, Object data) {
        ChannelGroup channels = CHANNEL_GROUP_MAP.get(groupKey);
        if (channels != null) {
            Map result = Result.createMap(Fields.DATA, data, Fields.KEY, groupKey);
            channels.writeAndFlush(new TextWebSocketFrame(JSONUtil.toJSONString(result)));
        }
    }

    public static void send(Channel channel, Object data, String key) {
        Map result = Result.createMap(Fields.DATA, data, Fields.KEY, key);
        channel.writeAndFlush(new TextWebSocketFrame(JSONUtil.toJSONString(result)));
    }

    /**
     * 建立连接
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        CHANNEL_GROUP_MAP.get(ChatConstant.DEFAULT).add(ctx.channel());
    }

    /**
     * 通道关闭
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        CHANNEL_GROUP_MAP.forEach((key, val) -> {
            if (val.size() == 0) {
                CHANNEL_GROUP_MAP.remove(key);
            }
        });
    }
}
